package com.songshuang.myutilsboot.stlf.core;

import com.songshuang.myutilsboot.stlf.model.Models;

import java.util.*;

public final class ExpsPredictor {
    private ExpsPredictor() {}

    public static boolean predict(Models.InputParameter ip, Models.ForecastInfo fi) {
        if (ip.global_rc == null) return false;
        int sdnum = (int) (ip.global_rc.sdnum > 0 ? ip.global_rc.sdnum : 96);
        if (ip.his_data == null || ip.his_data.isEmpty()) return false;

        // 历史序列：按日期顺序，每个 sd 形成一个历史序列
        Map<String, Map<Long, Models.HisData>> byDate = new LinkedHashMap<>();
        for (Models.HisData d : ip.his_data) byDate.computeIfAbsent(d.Date, k -> new HashMap<>()).put(d.Sd, d);
        int hisDays = byDate.size();
        // 组织历史矩阵 his[day][sd]
        double[][] his = new double[hisDays + 1][sdnum + 1];
        int di = 0;
        for (Map.Entry<String, Map<Long, Models.HisData>> e : byDate.entrySet()) {
            di++;
            Map<Long, Models.HisData> sdMap = e.getValue();
            for (int sd = 1; sd <= sdnum; sd++) {
                Models.HisData d = sdMap.getOrDefault((long) sd, defaultHis(e.getKey(), sd));
                his[di][sd] = d.MW0;
            }
        }

        // 对每个 sd 做二次指数平滑，预测一步
        double[] foreOne = new double[sdnum + 1];
        for (int sd = 1; sd <= sdnum; sd++) {
            double[] series = new double[hisDays + 1];
            for (int i = 1; i <= hisDays; i++) series[i] = his[i][sd];
            double pred = expsForecast(series, hisDays, 0.3); // EXP_C=0.3 参考 C++
            foreOne[sd] = pred;
        }

        // 生成 His_Report
        List<Models.SigSdInf> hisList = new ArrayList<>();
        di = 0;
        for (Map.Entry<String, Map<Long, Models.HisData>> e : byDate.entrySet()) {
            di++;
            String date = e.getKey();
            Map<Long, Models.HisData> sdMap = e.getValue();
            for (int sd = 1; sd <= sdnum; sd++) {
                Models.HisData d = sdMap.getOrDefault((long) sd, defaultHis(date, sd));
                Models.SigSdInf inf = new Models.SigSdInf();
                inf.Date = date; inf.Sd = sd; inf.MW0 = d.MW0; inf.MW = expsForecast(slice(seriesOfHis(his, sd), di), di, 0.3);
                inf.Err = inf.MW - inf.MW0; inf.Pct = inf.MW0 != 0 ? inf.Err * 100.0 / inf.MW0 : 0;
                hisList.add(inf);
            }
        }
        fi.His_Report = hisList;

        // 生成 Fore_Report（以 for_data 中的日期为键，填充 1..sdnum）
        Map<String, Map<Long, Models.ForData>> forByDate = new LinkedHashMap<>();
        if (ip.for_data != null) for (Models.ForData d : ip.for_data) forByDate.computeIfAbsent(d.Date, k -> new HashMap<>()).put(d.Sd, d);
        List<Models.SigSdInf> foreList = new ArrayList<>();
        for (Map.Entry<String, Map<Long, Models.ForData>> e : forByDate.entrySet()) {
            String date = e.getKey();
            for (int sd = 1; sd <= sdnum; sd++) {
                Models.SigSdInf inf = new Models.SigSdInf();
                inf.Date = date; inf.Sd = sd; inf.MW0 = 0; inf.MW = foreOne[sd]; inf.Err = 0; inf.Pct = 0;
                foreList.add(inf);
            }
        }
        fi.Fore_Report = foreList;
        return true;
    }

    private static double[] seriesOfHis(double[][] his, int sd) {
        double[] s = new double[his.length];
        for (int i = 1; i < his.length; i++) s[i] = his[i][sd];
        return s;
    }
    private static double[] slice(double[] s, int n) {
        double[] r = new double[n + 1];
        System.arraycopy(s, 0, r, 0, n + 1);
        return r;
    }

    private static double expsForecast(double[] x, int n, double wt) {
        // 二次指数平滑预测一步：f[n+1] = a[n] + b[n]
        double[] s1 = new double[n + 1];
        double[] s2 = new double[n + 1];
        for (int i = 1; i <= n; i++) {
            if (i == 1) { s1[i] = x[i]; s2[i] = x[i]; continue; }
            s1[i] = x[i] * wt + (1 - wt) * s1[i - 1];
            s2[i] = s1[i] * wt + (1 - wt) * s2[i - 1];
        }
        double a = 2 * s1[n] - s2[n];
        double b = (s1[n] - s2[n]) * wt / (1 - wt);
        return a + b;
    }

    private static Models.HisData defaultHis(String date, int sd) {
        Models.HisData d = new Models.HisData();
        d.Date = date; d.Sd = sd; d.MW0 = 0; return d;
    }
}


